package com.szgz.platform.service.impl;

import com.szgz.platform.service.SharedContainerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class SharedContainerServiceImpl implements SharedContainerService {

    private static final Logger logger = LoggerFactory.getLogger(SharedContainerServiceImpl.class);

    private final Set<Integer> usedPorts = ConcurrentHashMap.newKeySet();
    private int nextPort = 9000;

    @Override
    public int allocatePort() {
        int candidate = nextPort;
        while (true) {
            if (!usedPorts.contains(candidate) && isPortAvailable(candidate)) {
                usedPorts.add(candidate);
                nextPort = candidate + 1;
                logger.info("Allocated host port {}", candidate);
                return candidate;
            }
            candidate++;
        }
    }

    private boolean isPortAvailable(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            serverSocket.setReuseAddress(true);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    private String resolveDockerExecutable() {
        try {
            String fromEnv = System.getenv("DOCKER_PATH");
            if (fromEnv != null && !fromEnv.isBlank()) {
                return fromEnv.trim();
            }
            String[] candidates = new String[] {
                "/usr/bin/docker",
                "/usr/local/bin/docker",
                "docker",
                "docker.exe"
            };
            for (String c : candidates) {
                if (c.contains("/") || c.contains("\\")) {
                    try { if (java.nio.file.Files.exists(java.nio.file.Paths.get(c))) return c; } catch (Exception ignore) {}
                } else {
                    return c;
                }
            }
        } catch (Exception ignore) {}
        return "docker";
    }

    @Override
    public ProcessBuilder pbDocker(String... args) {
        java.util.List<String> cmd = new java.util.ArrayList<>();
        cmd.add(resolveDockerExecutable());
        cmd.addAll(java.util.Arrays.asList(args));
        return new ProcessBuilder(cmd);
    }

    @Override
    public void waitForContainerReady(String containerName, int timeoutSeconds) throws Exception {
        long deadline = System.currentTimeMillis() + timeoutSeconds * 1000L;
        Exception last = null;
        boolean isIdeContainer = containerName.contains("ide-shared");
        
        while (System.currentTimeMillis() < deadline) {
            try {
                // 首先检查容器是否在运行
                ProcessBuilder pbStatus = pbDocker("ps", "-q", "-f", "name=" + containerName);
                Process pStatus = pbStatus.start();
                pStatus.waitFor();
                
                // 读取输出检查容器是否存在
                java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(pStatus.getInputStream()));
                String containerId = reader.readLine();
                reader.close();
                
                if (containerId == null || containerId.trim().isEmpty()) {
                    throw new RuntimeException("Container " + containerName + " is not running");
                }
                
                // 对于IDE容器，优先检查健康状态
                if (isIdeContainer) {
                    ProcessBuilder pbHealth = pbDocker("inspect", "--format={{.State.Health.Status}}", containerName);
                    Process pHealth = pbHealth.start();
                    pHealth.waitFor();
                    
                    java.io.BufferedReader healthReader = new java.io.BufferedReader(new java.io.InputStreamReader(pHealth.getInputStream()));
                    String healthStatus = healthReader.readLine();
                    healthReader.close();
                    
                    if ("healthy".equals(healthStatus)) {
                        logger.info("Container {} is healthy and ready", containerName);
                        return;
                    } else if ("unhealthy".equals(healthStatus)) {
                        throw new RuntimeException("Container " + containerName + " is unhealthy");
                    }
                    // 如果健康检查还在进行中(starting)，继续等待
                    logger.debug("Container {} health status: {}", containerName, healthStatus);
                }
                
                // 备用检查：直接执行命令验证容器可用性
                String checkCommand;
                if (isIdeContainer) {
                    // 检查基本系统命令和启动脚本
                    checkCommand = "id -u >/dev/null 2>&1 && ls /usr/local/bin/start-code.sh >/dev/null 2>&1";
                } else {
                    // 其他容器使用原有的检查方式
                    checkCommand = "id -u >/dev/null 2>&1";
                }
                
                ProcessBuilder pb = pbDocker("exec", containerName, "sh", "-lc", checkCommand);
                Process p = pb.start();
                int exit = p.waitFor();
                if (exit == 0) {
                    logger.info("Container {} is ready (fallback check)", containerName);
                    return;
                }
            } catch (Exception e) {
                last = e;
                logger.debug("Container {} not ready yet: {}", containerName, e.getMessage());
            }
            Thread.sleep(3000); // 增加检查间隔到3秒
        }
        if (last != null) {
            throw new RuntimeException("Container not ready: " + containerName + ", last error=" + last.getMessage(), last);
        }
        throw new RuntimeException("Container not ready: " + containerName);
    }

    @Override
    public void persistPorts(String groupDir, Map<String, Integer> mapping) {
        try {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Integer> e : mapping.entrySet()) {
                sb.append(e.getKey()).append("=").append(e.getValue()).append("\n");
            }
            Files.write(Paths.get(groupDir + "/ports.map"), sb.toString().getBytes());
        } catch (Exception ex) {
            logger.warn("Failed to persist shared ports mapping: {}", ex.getMessage());
        }
    }

    @Override
    public Map<String, Integer> readPortsByGroupId(String groupId) {
        return readPortsByDir("./shared/" + groupId);
    }

    @Override
    public Map<String, Integer> readPortsByDir(String groupDir) {
        Map<String, Integer> map = new HashMap<>();
        try {
            java.nio.file.Path p = Paths.get(groupDir + "/ports.map");
            if (!Files.exists(p)) return map;
            for (String line : Files.readAllLines(p)) {
                if (line == null || line.isBlank() || !line.contains("=")) continue;
                String[] kv = line.split("=", 2);
                try { map.put(kv[0].trim(), Integer.parseInt(kv[1].trim())); } catch (Exception ignore) {}
            }
        } catch (Exception e) {
            logger.warn("Failed to read ports mapping for dir {}: {}", groupDir, e.getMessage());
        }
        return map;
    }

    @Override
    public void deleteDirectory(File dir) {
        if (dir.exists()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            dir.delete();
        }
    }
}

